Fork me on GitHub

Kafka原理学习

Kafka是一种中间件,基于Zookeeper。中间件是在操作系统、网络和数据库之上,应用软件的下层,总的作用是为处于自己上层的应用软件提供运行与开发的环境。对应到生活中的例子,有点类似于我们想把某个东西给朋友时,通过快递员送过去。消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。Kafka是一种高吞吐量的分布式消息队列,是一个分布式的、分区的、可靠的分布式日志存储服务。

消息队列的两种模式

  1. 点对点模式(一对一):消费者主动拉取数据,收到消息后,消息会清除;缺点是消费者需要实时监控生产者。
  2. 发布/订阅模式(一对多):生产数据后,会推送给所有的订阅者。

Kafka好处

  • 可靠性: Kafka是分布式,分区,复制和容错的。
  • 可扩展性 : Kafka消息传递系统轻松缩放,无需停机。
  • 耐用性 :Kafka使用分布式提交日志,这意味着消息会尽可能快地保留在磁盘上,因此它是持久的。
  • 性能:Kafka对于发布和订阅消息都具有高吞吐量。 即使存储了许多TB的消息,它也保持稳定的性能。

Kafka的使用场景

  • 日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、Hbase、Solr等。
  • 消息系统:解耦和生产者和消费者、缓存消息等。
  • 用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。
  • 运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。

Kafka 中的术语

  • broker:Kafka节点,用于存储消息,一个Kafka节点就是一个broker,多个broker可以组成一个Kafka集群

  • topic:kafka给消息提供的分类方式。broker用来存储不同topic的消息数据。

  • Partition:topic物理上的分组,一个topic可以分为多个partition,每个partition是一个有序的队列。

  • Segment:partition物理上由多个segment组成,每个Segment存着message信息。

  • producer:往broker中某个topic里面生产数据。

  • consumer:从broker中某个topic获取数据。1个消费者可以消费多个Topic。

  • Consumer Group:一个Consumer Group包含多个consumer, 这个是预先在配置文件中配置好的。各个consumer(consumer 线程)可以组成一个组(Consumer group ),partition中的每个message只能被组(Consumer group ) 中的一个consumer(consumer 线程 )消费,如果一个message可以被多个consumer(consumer 线程 ) 消费的话,那么这些consumer必须在不同的组。

  • ISR:In-Sync Replicas 副本同步队列,由leader维护。消息会先存在leader节点,然后其余副本节点从leader节点复制数据,当leader节点挂掉的时候,会从ISR中找到和leader相似度最高的节点来取代它;如果一个follower比一个leader落后太多,或者超过一定时间未发起数据复制请求,则leader将其从ISR中移除。

    每个Partition有一个leader与多个follower,producer往某个Partition中写入数据是,只会往leader中写入数据,然后数据才会被复制进其他的Replica中。

    数据是由leader push过去还是有follower pull过来? pull
    kafka是由follower周期性或者尝试去pull(拉)过来(其实这个过程与consumer消费过程非常相似),写是都往leader上写,但是读并不是任意follower上读都行,读也只在leader上读,follower只是数据的一个备份,保证leader被挂掉后顶上来,并不往外提供服务。

    follower如何与leader同步?ISR机制

    完全同步复制要求All Alive Follower都复制完,这条消息才会被认为commit,这种复制方式极大的影响了吞吐率。

    异步复制方式下,Follower异步的从Leader复制数据,数据只要被Leader写入log就被认为已经commit,这种情况下,如果leader挂掉,会丢失数据。

    kafka不是完全同步,也不是完全异步,是一种ISR机制:

    1. leader会维护一个与其基本保持同步的Replica列表,该列表称为ISR(in-sync Replica),每个Partition都会有一个ISR,而且是由leader动态维护
    2. 如果一个follower比一个leader落后太多,或者超过一定时间未发起数据复制请求,则leader将其重ISR中移除
    3. 当ISR中所有Replica都向Leader发送ACK时,leader才commit

    如果leader挂掉了会发生什么?

    如果leader挂掉了,从它的follower中选举一个作为leader,并把挂掉的leader从ISR中移除,继续处理数据。一段时间后该leader重新启动了,它知道它之前的数据到哪里了,尝试获取它挂掉后leader处理的数据,获取完成后它就加入了ISR。

Topic中的partition分区(Kafka这么快的原因之一)

在Kafka文件存储中,同一个topic下有多个不同partition,每个partition为一个目录,partiton命名规则为topic名称+有序序号,第一个partiton序号从0开始,序号最大值为partitions数量减1。
消息发送时都被发送到一个topic,其本质就是一个目录,而topic由是由一些Partition组成。

Partition的好处是把topic进一步划分,在生产者产生数据时可以指定把数据发送到topic的指定Partition中,这样可以更加高效的读取数据,并且也可以存放更多的数据(因为存储的压力被分散了)。

Partition是一个Queue的结构,每个Partition中的消息都是有序的,生产的消息被不断追加到Partition上,其中的每一个消息都被赋予了一个唯一的offset值。

Kafka只维护在partition中的offset值,因为这个offsite标识着这个partition的message消费到哪条了。Consumer每消费一个消息,offset就会加1。其实消息的状态完全是由Consumer控制的,Consumer可以跟踪和重设这个offset值,这样的话Consumer就可以读取任意位置的消息。

同一group内,一个partition只能被一个消费者消费(一个消费者可以同时消费多个partition),因此,如果设置的partition的数量小于consumer的数量,就会有消费者消费不到数据。所以,推荐partition的数量一定要大于同时运行的consumer的数量。

Kafka集群会保存所有的消息,不管消息有没有被消费;我们可以设定消息的过期时间,只有过期的数据才会被自动清除以释放磁盘空间。

生产者写消息的过程

在 Kafka 中,生产者写入消息、消费者读取消息的操作都是与 leader 副本进行交互的,从 而实现的是一种主写主读的生产消费模型。

生产者采用push的模式,把消息发布到broker,每条消息都会被append到partition中,是顺序写磁盘,效率比随机写内存要高。每个分区自己的内部是有序的,但分区之间的顺序是不一定的,每条消息存入哪个一partition,可能是被指定的、哈希的、或者是轮询的。

消息发送的ACK机制

ACK=1(默认)

数据发送到Kafka后,经过leader成功接收消息的的确认,就算是发送成功了。在这种情况下,如果leader宕机了,则会丢失数据。

ACK=0

生产者将数据发送出去就不管了,不去等待任何返回。这种情况下数据传输效率最高,但是数据可靠性确是最低的。

ACK=-1

producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。当ISR中所有Replica都向Leader发送ACK时,leader才commit,这时候producer才能认为一个请求中的消息都commit了。

零拷贝技术(Kafka这么快的原因之一)

通常情况下,Kafka的消息会有多个订阅者,生产者发布的消息会被不同的消费者多次消费,为了优化这个流程,Kafka使用了“零拷贝技术”。有了DMA后,就可以实现绝对的零拷贝了,因为网卡是直接去访问系统主内存的,减少了用户态与内核态的切换,让拷贝的次数降到最低,所以效率更高。

传统的文件拷贝

传统的文件拷贝通常需要从用户态去转到核心态,经过核心态的read buffer,然后再返回到用户态的应用层buffer,然后再从用户态的应用层buffer把数据拷贝到核心态的socket buffer,然后发送到网卡。需要多次的用户态和核心态之间的切换,而且还要把数据复制多次,最终才打到网卡。

顺序写入(Kafka这么快的原因之一)

每一个Partition其实都是一个文件 ,收到消息后Kafka会把数据插入到文件末尾。这种方法有一个缺陷—— 没有办法删除数据 ,所以Kafka是不会删除读取过的旧数据的,它会把所有的数据都保留下来,每个消费者(Consumer)对每个Topic都有一个offset用来表示读取到了第几条数据 。

如果一直不删除硬盘肯定会被撑满,所以Kakfa提供了两种策略来删除数据。一是基于时间,二是基于partition文件大小。每一个partition目录下的文件被平均切割成大小相等(默认一个文件是500兆,可以手动去设置)的数据文件,每一个数据文件都被称为一个段(segment file),但每个段消息数量不一定相等,这种特性能够使得老的segment可以被快速清除。

批量发送和数据压缩(Kafka这么快的原因之一)

kafka可以设置批量发送数据,当生产者产生数据达到一定数量,或者没有达到数量,但是达到一定时间间隔,就会一次将数据发送给kafka的broker,因为是批量的原因,减少了网络交互次数,节省很多时间。如果宽带有压力,还可以将批量数据进行压缩处理。

-------------本文结束感谢您的阅读-------------